#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "sxml_parse.h"

//Local variables
static CALLBACK_META callback_meta = NULL;
static CALLBACK_TAGS callback_tags = NULL;
static CALLBACK_DATA callback_data = NULL;
static CALLBACK_PROP callback_prop = NULL;

//Split tag and property
static void sxmlProcessTag(int* level, const char* tag)
{
    char c = 0;

    //status for remember in "" like string.
    char binstring = 0;
    //string index and buffer
    int string_index = 0;
    char string_buffer[1024] = {0};
    char string_embedded[1024] = {0};
    char string_property[1024] = {0};

    //first tag char
    char tagfirst = tag[0];
    char tagend = 0;
    
    int len = 0;
    
    //Get last char (at tail)
    len = strlen(tag);
    if (len > 0) {
        tagend = tag[len - 1];
    } else {
        tagend = '\0';
    }

    //Determine type of tag
    if ((tagfirst != '/') && (tagfirst != '?')) {
        (*level)++;
    }

    //Ignore meta tag
    if (tagfirst == '?') {
        if (callback_meta != NULL) {
            callback_meta(*level, tag);
        }
        return;
    }

    //Split tag from tag+prop
    while((*tag != '\0') && (*tag != ' ')) {
        string_buffer[string_index] = *tag++;
        string_index++;
    }
    string_buffer[string_index++] = '\0';

    if (*tag == ' ') {
        tag++;
    }

    //copy tags
    if (tagend == '/') {
        strcpy(string_embedded, "/");
        strcat(string_embedded, string_buffer);
    }

    //indicat found tags
    if (callback_tags != NULL) {
        callback_tags(*level, string_buffer);
    }

    //Process property
    if (*tag != '\0') {
        string_index = 0;
        while((c = *tag++) != '\0') {
            switch(c) {
                case '=':
                    if (string_index > 0) {
                        string_buffer[string_index++] = '\0';
                        strcpy(string_property, string_buffer);
                        string_index = 0;
                    }
                    break;
                case ' ':
                    if ((!binstring) && (string_index > 0)) {
                        string_buffer[string_index++] = '\0';
                        strcpy(string_property, string_buffer);
                        string_index = 0;
                    }
                    break;
                case '"':
                    if (binstring) {
                        string_buffer[string_index++] = '\0';
                        if (callback_prop != NULL) {
                            callback_prop(*level, string_property, string_buffer);
                        }
                        binstring = 0;
                        string_index = 0;
                    } else {
                        binstring = 1;
                    }
                    break;
                default:
                    string_buffer[string_index++] = c;
                    break;
            }//end of switch(c) {
        }//end of while((c = *tag++) != '\0') {

        //When property value end with not "" like string,
        //must check and process one more time.
        if ((tagend != '/') && (!binstring) && (string_index > 0)) {
            string_buffer[string_index++] = '\0';
            if (callback_prop != NULL) {
                callback_prop(*level, string_property, string_buffer);
            }
            string_index = 0;
        }
    }

    //Change level
    if (tagend == '/') {
        callback_tags(*level, string_embedded);
    }
    if ((tagfirst == '/') || (tagend == '/')) {
        (*level)--;
    }
}

int sxmlInitCallback(CALLBACK_META cb_meta, CALLBACK_TAGS cb_tags,
        CALLBACK_DATA cb_data, CALLBACK_PROP cb_prop)
{
    callback_meta = cb_meta;
    callback_tags = cb_tags;
    callback_data = cb_data;
    callback_prop = cb_prop;

    return 0;
}

int sxmlParseFromFile(const char* xmlfile)
{
    char c = 0;

    int level = 0;
    int string_index = 0;
    char string_buffer[1024] = {0};

    FILE* fp = NULL;
    if ((fp = fopen(xmlfile, "r")) == NULL) {
        perror("Can't open file.");
        return -1;
    }

    //Process input file
    while (!feof(fp)) {
        c = fgetc(fp);
        //rule:
        // <> must match.
        // "" must match.
        // Must not nested!
        switch(c) {
            case '<':
                if (string_index > 0) {
                    string_buffer[string_index++] = '\0';
                    if (callback_data != NULL) {
                        callback_data(level, string_buffer);
                    }
                }
                string_index = 0;
                break;
            case '>':
                string_buffer[string_index++] = '\0';
                sxmlProcessTag(&level, string_buffer);
                string_index = 0;
                break;
            default:
                //Ignore all white space before tag and data!
                if ((string_index == 0) && (isspace(c))) {
                } else {
                    string_buffer[string_index++] = c;
                }
                break;
        }

    }

    fclose(fp);
    fp = NULL;

    return 0;
}

